-
Notifications
You must be signed in to change notification settings - Fork 1
Feat/#51 Vercel 배포 관련 작업 #52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- ssg로 페이지 생성 시 현재 서버 미구축(에러 발생)으로 인한 빌드 실패 우회
- URL과 UI 상태가 항상 1:1로 동기화 - replaceState을 사용해 깜빡임 방지 - metaData 추가
🚀 코드 리뷰 분석Walkthrough다양한 페이지 컴포넌트에 Next.js 메타데이터 및 동적 렌더링 설정을 추가하고, 카테고리 상세 페이지를 경로 기반 상태 해결로 리팩토링하며, 지도 컴포넌트를 SSR 비활성화로 재구성합니다. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (2)
apps/web/app/requests/page.tsx (1)
13-13: 불필요한 async 선언을 제거하는 것을 고려하세요.Page 컴포넌트가
async로 선언되었으나, 내부에서await를 사용하지 않습니다.HydrationBoundaryPage의prefetchprop이 이미 비동기 함수를 처리하므로, Page 컴포넌트 자체를 async로 만들 필요는 없습니다.다음 diff를 적용하여 불필요한 async를 제거할 수 있습니다:
-const Page = async () => { +const Page = () => {apps/web/app/events/food-slot/page.tsx (1)
14-23: Open Graph 타이틀 형식의 불일치가 확인되었습니다.코드베이스 전체에서 두 가지 형식이 혼용되고 있습니다:
- 형식 A (
공주대 맛집 | ${pageTitle}):apps/web/app/events/food-slot/page.tsx:18,apps/web/app/places/[id]/page.tsx:22,apps/web/app/categories/[id]/page.tsx:21- 형식 B (
${pageTitle} | 공주대 맛집):apps/web/app/likes/page.tsx:18,apps/web/app/map/page.tsx:13전체 앱에서 하나의 형식으로 통일하는 것을 권장합니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
apps/web/app/favicon.icois excluded by!**/*.ico
📒 Files selected for processing (14)
apps/web/app/categories/[id]/CategoryDetailPage.tsx(3 hunks)apps/web/app/categories/[id]/page.tsx(1 hunks)apps/web/app/events/food-slot/page.tsx(1 hunks)apps/web/app/events/lucky-draw/page.tsx(1 hunks)apps/web/app/layout.tsx(1 hunks)apps/web/app/likes/page.tsx(1 hunks)apps/web/app/map/MapComponent.tsx(2 hunks)apps/web/app/map/MapContainer.tsx(1 hunks)apps/web/app/map/page.tsx(1 hunks)apps/web/app/page.tsx(1 hunks)apps/web/app/places/[id]/page.tsx(1 hunks)apps/web/app/places/new/page.tsx(1 hunks)apps/web/app/requests/[id]/page.tsx(1 hunks)apps/web/app/requests/page.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (12)
apps/web/app/map/MapContainer.tsx (2)
apps/web/app/map/MapPage.tsx (1)
map(21-104)apps/web/app/NaverMapProvider.tsx (1)
children(6-10)
apps/web/app/requests/[id]/page.tsx (6)
apps/web/app/likes/page.tsx (1)
dynamic(9-9)apps/web/app/events/food-slot/page.tsx (1)
dynamic(8-8)apps/web/app/events/lucky-draw/page.tsx (1)
dynamic(11-11)apps/web/app/page.tsx (1)
dynamic(23-23)apps/web/app/places/new/page.tsx (1)
dynamic(53-53)apps/web/app/requests/page.tsx (1)
dynamic(11-11)
apps/web/app/places/new/page.tsx (6)
apps/web/app/likes/page.tsx (1)
dynamic(9-9)apps/web/app/events/food-slot/page.tsx (1)
dynamic(8-8)apps/web/app/events/lucky-draw/page.tsx (1)
dynamic(11-11)apps/web/app/page.tsx (1)
dynamic(23-23)apps/web/app/requests/[id]/page.tsx (1)
dynamic(5-5)apps/web/app/requests/page.tsx (1)
dynamic(11-11)
apps/web/app/likes/page.tsx (6)
apps/web/app/events/food-slot/page.tsx (2)
dynamic(8-8)metadata(14-23)apps/web/app/events/lucky-draw/page.tsx (1)
dynamic(11-11)apps/web/app/page.tsx (1)
dynamic(23-23)apps/web/app/places/new/page.tsx (1)
dynamic(53-53)apps/web/app/requests/[id]/page.tsx (1)
dynamic(5-5)apps/web/app/requests/page.tsx (1)
dynamic(11-11)
apps/web/app/page.tsx (6)
apps/web/app/likes/page.tsx (1)
dynamic(9-9)apps/web/app/events/food-slot/page.tsx (1)
dynamic(8-8)apps/web/app/events/lucky-draw/page.tsx (1)
dynamic(11-11)apps/web/app/places/new/page.tsx (1)
dynamic(53-53)apps/web/app/requests/[id]/page.tsx (1)
dynamic(5-5)apps/web/app/requests/page.tsx (1)
dynamic(11-11)
apps/web/app/categories/[id]/CategoryDetailPage.tsx (3)
apps/web/app/_apis/queries/category.ts (1)
useCategoryQueries(11-17)apps/web/app/categories/[id]/_components/RowCategories/RowCategories.tsx (1)
RowCategories(12-30)apps/web/app/categories/[id]/_components/Places/Places.tsx (1)
Places(8-30)
apps/web/app/events/food-slot/page.tsx (6)
apps/web/app/likes/page.tsx (2)
dynamic(9-9)metadata(14-23)apps/web/app/events/lucky-draw/page.tsx (1)
dynamic(11-11)apps/web/app/page.tsx (1)
dynamic(23-23)apps/web/app/places/new/page.tsx (1)
dynamic(53-53)apps/web/app/requests/[id]/page.tsx (1)
dynamic(5-5)apps/web/app/requests/page.tsx (1)
dynamic(11-11)
apps/web/app/categories/[id]/page.tsx (4)
apps/web/app/_apis/services/category.ts (1)
getCategories(5-8)apps/web/app/HydrationBoundaryPage.tsx (1)
HydrationBoundaryPage(29-44)apps/web/app/_apis/queries/category.ts (1)
useCategoryQueries(11-17)apps/web/app/categories/[id]/CategoryDetailPage.tsx (1)
CategoryDetailPage(15-55)
apps/web/app/requests/page.tsx (3)
apps/web/app/likes/page.tsx (1)
dynamic(9-9)apps/web/app/page.tsx (2)
dynamic(23-23)Page(25-59)apps/web/app/requests/[id]/page.tsx (1)
dynamic(5-5)
apps/web/app/events/lucky-draw/page.tsx (6)
apps/web/app/likes/page.tsx (1)
dynamic(9-9)apps/web/app/events/food-slot/page.tsx (1)
dynamic(8-8)apps/web/app/page.tsx (1)
dynamic(23-23)apps/web/app/places/new/page.tsx (1)
dynamic(53-53)apps/web/app/requests/[id]/page.tsx (1)
dynamic(5-5)apps/web/app/requests/page.tsx (1)
dynamic(11-11)
apps/web/app/places/[id]/page.tsx (1)
apps/web/app/_apis/services/place.ts (1)
getPlaceDetail(60-63)
apps/web/app/map/page.tsx (1)
apps/web/app/map/MapContainer.tsx (1)
MapContainer(10-10)
🔇 Additional comments (13)
apps/web/app/page.tsx (1)
23-23: 동적 렌더링 설정이 올바르게 적용되었습니다.
force-dynamic설정으로 빌드 시 API 호출을 방지하여 MSW 미실행 문제를 해결했습니다.apps/web/app/requests/[id]/page.tsx (1)
5-5: LGTM!동적 렌더링 설정이 올바르게 적용되었습니다.
apps/web/app/events/lucky-draw/page.tsx (1)
11-11: LGTM!동적 렌더링 설정이 올바르게 적용되었습니다.
apps/web/app/events/food-slot/page.tsx (1)
8-8: LGTM!동적 렌더링 설정이 올바르게 적용되었습니다.
apps/web/app/likes/page.tsx (2)
9-9: LGTM!동적 렌더링 설정이 올바르게 적용되었습니다.
14-23: 메타데이터가 올바르게 구현되었습니다.페이지 제목, 설명, Open Graph 데이터가 적절하게 설정되었습니다.
apps/web/app/requests/page.tsx (1)
11-11: LGTM!동적 렌더링 설정이 올바르게 적용되었습니다.
apps/web/app/layout.tsx (1)
12-32: 메타데이터 구성이 잘 되어 있습니다.Next.js 메타데이터 API를 활용하여
metadataBase, 타이틀 템플릿, 설명, Open Graph 설정이 올바르게 구성되었습니다. 타이틀 템플릿을 통해 하위 페이지에서 일관된 제목 형식을 유지할 수 있습니다.apps/web/app/map/MapContainer.tsx (1)
1-10: SSR 비활성화 처리가 적절합니다.Naver Maps가 window 객체를 필요로 하기 때문에
dynamicimport에ssr: false설정을 통해 클라이언트 사이드에서만 렌더링하도록 한 것은 올바른 접근입니다. 로딩 폴백도 제공하여 사용자 경험을 개선했습니다.apps/web/app/map/page.tsx (2)
6-18: 메타데이터가 올바르게 구성되었습니다.페이지 타이틀, 설명, Open Graph 정보가 적절하게 설정되었습니다. 지도 페이지의 SEO 최적화에 도움이 됩니다.
20-28: MapContainer 사용이 적절합니다.이전의 MapPage를 MapContainer로 교체하여 클라이언트 사이드 렌더링을 보장합니다. 이 페이지는 HydrationBoundary를 사용하지 않으므로
dynamic = 'force-dynamic'설정이 필요하지 않습니다.apps/web/app/map/MapComponent.tsx (1)
21-21: 컴포넌트 리네이밍이 적절합니다.MapPage에서 MapComponent로 이름을 변경하고 default export로 전환하여
MapContainer의 dynamic import와 잘 연동됩니다.apps/web/app/categories/[id]/CategoryDetailPage.tsx (1)
50-52: Suspense 래퍼 추가가 적절합니다.Places 컴포넌트가
useSuspenseQuery를 사용하므로 Suspense 경계로 감싸는 것이 올바릅니다. 로딩 상태에서 Spinner를 표시하여 사용자 경험을 개선합니다.
| const setIdFunc = (id: string) => { | ||
| setId(id) | ||
| window.history.replaceState(null, '', `/categories/${id}`) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Next.js 라우터 사용을 고려하세요.
window.history.replaceState를 직접 사용하는 것은 Next.js의 클라이언트 사이드 라우팅을 우회합니다. 이는 프리페칭, 스크롤 복원 등의 Next.js 기능과 충돌할 수 있습니다.
다음 스크립트를 실행하여 Next.js router를 사용하는 다른 예시가 있는지 확인하세요:
next/navigation의 useRouter를 사용하는 것이 더 적절할 수 있습니다:
import { useRouter } from 'next/navigation'
const router = useRouter()
const setIdFunc = (id: string) => {
router.replace(`/categories/${id}`)
}🏁 Script executed:
#!/bin/bash
# Next.js useRouter 사용 패턴 검색
rg -n "useRouter|router\.push|router\.replace" --type=tsx --type=ts -C 3Length of output: 27
새로운 스크립트를 실행하여 코드베이스를 재검증하겠습니다.
---
🏁 Script executed:
```shell
#!/bin/bash
# TypeScript/TSX 파일에서 router 패턴 검색 (파일 타입 제약 제거)
rg -n "useRouter|router\.push|router\.replace|window\.history" -C 3 --glob="*.tsx" --glob="*.ts"
Length of output: 4609
🏁 Script executed:
#!/bin/bash
# 해당 파일 확인
fd "CategoryDetailPage.tsx" -type fLength of output: 170
🏁 Script executed:
#!/bin/bash
# 파일 내용 확인
find . -name "CategoryDetailPage.tsx" -type f -exec cat {} \;Length of output: 1746
Next.js 라우터 사용으로 변경하세요.
이 파일은 이미 'use client' 선언이 있고 next/navigation에서 usePathname을 임포트하고 있습니다. 코드베이스의 다른 컴포넌트들(HeaderHomeButton.tsx, HeaderBackButton.tsx 등)도 useRouter를 사용하여 네비게이션을 처리하고 있습니다.
window.history.replaceState를 직접 사용하면 Next.js의 클라이언트 사이드 라우팅 기능(프리페칭, 스크롤 복원 등)이 제대로 작동하지 않을 수 있습니다.
다음과 같이 수정하세요:
import { useRouter } from 'next/navigation'
const router = useRouter()
const setIdFunc = (id: string) => {
router.replace(`/categories/${id}`)
}🤖 Prompt for AI Agents
In apps/web/app/categories/[id]/CategoryDetailPage.tsx around lines 22 to 24,
replace the direct window.history.replaceState usage with Next.js client router
navigation: import and call useRouter from next/navigation, get router via
useRouter() in the component, and change setIdFunc to call
router.replace(`/categories/${id}`) so routing uses Next.js client-side
navigation and preserves prefetching/scroll restoration.
| useEffect(() => { | ||
| document.title = `공주대 맛집 | ${activeCategory?.name}` | ||
| }, [activeCategory]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
document.title 직접 조작을 제거하세요.
page.tsx에서 generateMetadata를 통해 메타데이터를 이미 관리하고 있으므로, 클라이언트 컴포넌트에서 document.title을 직접 설정하는 것은 불필요하며 Next.js의 메타데이터 시스템과 충돌할 수 있습니다.
다음과 같이 useEffect를 제거하세요:
- useEffect(() => {
- document.title = `공주대 맛집 | ${activeCategory?.name}`
- }, [activeCategory])
-📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| useEffect(() => { | |
| document.title = `공주대 맛집 | ${activeCategory?.name}` | |
| }, [activeCategory]) |
🤖 Prompt for AI Agents
In apps/web/app/categories/[id]/CategoryDetailPage.tsx around lines 26 to 28,
remove the client-side useEffect that sets document.title (document.title = `공주대
맛집 | ${activeCategory?.name}`) because page.tsx already manages metadata via
generateMetadata; delete the entire useEffect block and any imports or hooks
that are only used for that effect so the component relies on Next.js metadata
instead.
| export async function generateMetadata({ | ||
| params, | ||
| }: { | ||
| params: Promise<{ id: string }> | ||
| }): Promise<Metadata> { | ||
| const { id: categoryId } = await params | ||
|
|
||
| const data = await getCategories() | ||
| const categoryName = data?.find(({ id }) => id === categoryId)?.name | ||
|
|
||
| return { | ||
| title: `${categoryName}`, | ||
| description: `공주대학교 근처 ${categoryName} 맛집`, | ||
| openGraph: { | ||
| title: `공주대 맛집 | ${categoryName}`, | ||
| description: `공주대학교 근처 ${categoryName} 맛집`, | ||
| locale: 'ko-KR', | ||
| type: 'website', | ||
| }, | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
generateMetadata에 에러 핸들링을 추가하세요.
카테고리를 찾지 못했을 때를 대비한 에러 핸들링이 필요합니다. categoryName이 undefined일 경우 메타데이터에 "undefined"가 포함될 수 있습니다.
다음과 같이 폴백을 추가하세요:
export async function generateMetadata({
params,
}: {
params: Promise<{ id: string }>
}): Promise<Metadata> {
const { id: categoryId } = await params
const data = await getCategories()
- const categoryName = data?.find(({ id }) => id === categoryId)?.name
+ const categoryName = data?.find(({ id }) => id === categoryId)?.name || '카테고리'
return {
title: `${categoryName}`,
description: `공주대학교 근처 ${categoryName} 맛집`,
openGraph: {
title: `공주대 맛집 | ${categoryName}`,
description: `공주대학교 근처 ${categoryName} 맛집`,
locale: 'ko-KR',
type: 'website',
},
}
}🤖 Prompt for AI Agents
In apps/web/app/categories/[id]/page.tsx around lines 7 to 27, generateMetadata
currently assumes categoryName exists and may insert "undefined" into metadata;
update the function to handle missing categories by checking if categoryName is
undefined and either (a) use a safe fallback string (e.g., '카테고리' or 'Unknown')
when building title/description/openGraph, or (b) throw a notFound()/404 so the
page metadata isn't generated for a non-existent category; implement one of
these branches and ensure all metadata fields use the fallback/handled value.
| const Page = () => { | ||
| return ( | ||
| <HydrationBoundaryPage | ||
| prefetch={async (queryClient) => { | ||
| await queryClient.prefetchQuery(useCategoryQueries.list()) | ||
| }} | ||
| > | ||
| <CategoryDetailPage initId={id} /> | ||
| <CategoryDetailPage /> | ||
| </HydrationBoundaryPage> | ||
| ) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HydrationBoundary를 사용하는 페이지에 dynamic 설정이 누락되었습니다.
PR 설명에 따르면 HydrationBoundary를 사용하는 페이지에 export const dynamic = 'force-dynamic'을 추가하여 빌드 타임 API 요청을 방지해야 합니다. 이 설정이 없으면 Next.js가 SSG로 빌드 시 MSW가 실행되지 않아 500 에러가 발생할 수 있습니다.
다음과 같이 dynamic export를 추가하세요:
import type { Metadata } from 'next'
import { HydrationBoundaryPage } from '@/HydrationBoundaryPage'
import { useCategoryQueries } from '@/_apis/queries/category'
import { CategoryDetailPage } from '@/categories/[id]/CategoryDetailPage'
import { getCategories } from '@/_apis/services/category'
+export const dynamic = 'force-dynamic'
+
export async function generateMetadata({Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/web/app/categories/[id]/page.tsx around lines 29 to 39, the page using
HydrationBoundaryPage is missing the Next.js dynamic export required to force
dynamic rendering; add a top-level export: export const dynamic =
'force-dynamic' in this file (placed at module scope, above the component or
exports) so build-time SSG is disabled and MSW-backed API calls run at request
time to avoid the 500 error.
| export async function generateMetadata({ | ||
| params, | ||
| }: { | ||
| params: Promise<{ id: string }> | ||
| }): Promise<Metadata> { | ||
| const { id } = await params | ||
|
|
||
| const data = await getPlaceDetail(id) | ||
| const { placeName } = data | ||
| const pageDescription = `공주대학교 근처 ${placeName} 맛집 상세 정보` | ||
|
|
||
| return { | ||
| title: `${placeName}`, | ||
| description: pageDescription, | ||
| openGraph: { | ||
| title: `공주대 맛집 | ${placeName}`, | ||
| description: pageDescription, | ||
| locale: 'ko-KR', | ||
| type: 'website', | ||
| }, | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
메타데이터 생성이 올바르게 구현되었습니다.
generateMetadata에서 getPlaceDetail을 호출하고, Page 컴포넌트에서 usePlaceQueries.detail로 동일한 데이터를 프리페치하는 것이 중복처럼 보일 수 있으나, Next.js 15는 동일한 요청을 자동으로 중복 제거(dedupe)하므로 실제로는 한 번만 호출됩니다. 이는 Next.js의 권장 패턴입니다.
다만, export const dynamic = 'force-dynamic'이 이 파일에 없는데, HydrationBoundaryPage를 사용하는 다른 페이지들과 일관성을 위해 추가를 고려해보세요.
다음 스크립트로 HydrationBoundaryPage를 사용하면서 dynamic export가 없는 페이지를 확인하세요:
🏁 Script executed:
#!/bin/bash
# HydrationBoundaryPage를 사용하지만 dynamic export가 없는 파일 찾기
for file in $(rg -l "HydrationBoundaryPage" --type=tsx --type=ts apps/web/app); do
if ! grep -q "export const dynamic" "$file"; then
echo "Missing dynamic export: $file"
fi
doneLength of output: 27
고마워요. 스크립트에 문제가 있네요. rg의 파일 타입을 수정하겠습니다.
---
🏁 Script executed:
```shell
#!/bin/bash
# tsx/ts 파일에서 HydrationBoundaryPage를 사용하는 모든 파일 찾기
# 그리고 export const dynamic 여부 확인
echo "=== Files using HydrationBoundaryPage ==="
rg "HydrationBoundaryPage" -t typescript apps/web/app --files-with-matches
echo ""
echo "=== Checking for dynamic export in places/[id]/page.tsx ==="
if grep -q "export const dynamic" apps/web/app/places/\[id\]/page.tsx; then
echo "✓ Has dynamic export"
else
echo "✗ Missing dynamic export"
fi
echo ""
echo "=== All HydrationBoundaryPage files with dynamic export status ==="
for file in $(rg "HydrationBoundaryPage" -t typescript apps/web/app --files-with-matches); do
if grep -q "export const dynamic" "$file"; then
echo "✓ $file"
else
echo "✗ $file"
fi
done
Length of output: 265
파일 타입 지정 문제가 있네요. 다른 방식으로 접근하겠습니다.
🏁 Script executed:
#!/bin/bash
# 파일 타입 필터링 없이 HydrationBoundaryPage 검색
echo "=== Files using HydrationBoundaryPage ==="
rg "HydrationBoundaryPage" apps/web/app -l
echo ""
echo "=== Current file status ==="
echo "File: apps/web/app/places/[id]/page.tsx"
if grep -q "export const dynamic" "apps/web/app/places/[id]/page.tsx"; then
echo "Dynamic export: ✓ Present"
else
echo "Dynamic export: ✗ Missing"
fi
echo ""
echo "=== Checking all identified files for dynamic export ==="
rg "HydrationBoundaryPage" apps/web/app -l | while read file; do
if grep -q "export const dynamic" "$file"; then
echo "✓ HAS: $file"
else
echo "✗ LACKS: $file"
fi
doneLength of output: 877
export const dynamic = 'force-dynamic' 추가 필요
검증 결과, apps/web/app/places/[id]/page.tsx는 실제로 export const dynamic 선언이 누락되어 있습니다. HydrationBoundaryPage를 사용하는 다른 페이지들을 확인한 결과:
- 동적 선언 있음:
page.tsx,requests/page.tsx,requests/[id]/page.tsx,likes/page.tsx,events/food-slot/page.tsx,events/lucky-draw/page.tsx(6개) - 동적 선언 없음:
places/[id]/page.tsx,categories/[id]/page.tsx(2개)
대부분의 HydrationBoundaryPage 사용 페이지가 동적 선언을 포함하고 있으므로, 일관성 유지를 위해 export const dynamic = 'force-dynamic'을 이 파일에 추가하세요.
🤖 Prompt for AI Agents
In apps/web/app/places/[id]/page.tsx around lines 7 to 28, add the missing
export const dynamic = 'force-dynamic' declaration to match other
HydrationBoundaryPage files; insert this export near the top of the module
(before the generateMetadata export) so the page is treated as dynamic and
consistent with the other pages using hydration boundaries.
#️⃣연관된 이슈
#51
📝작업 내용
1. HydrationBoundary를 사용하는 페이지 force-dynamic 추가 e77bb60
Next.js는 기본적으로 페이지를 **SSG(정적 렌더링)**로 빌드하며, 이 시점에 prefetch로 설정한 API 데이터를 미리 요청합니다.
하지만 현재 API는 MSW로 구현되어 있어, next build가 실행될 때는 모킹 API 서버가 동작하지 않습니다.
이로 인해 빌드 과정에서 API 요청이 500 에러 등을 반환하여 빌드가 실패했습니다.
현재 SSG 빌드 실패를 우회하기 위해, 데이터 페칭이 필요한 페이지에
export const dynamic = 'force-dynamic'플래그를 추가했습니다.렌더링 전략을 SSR로 변경하여, 빌드 시점의 API 호출을 건너뛰고 배포가 가능하도록 합니다.
2. 동적 지도 렌더링을 위한 MapComponent 및 MapContainer 추가 c66d438
지도를 사용하는 페이지의 경우, window 객체가 없는 서버 환경에서 렌더링을 시도하다 window is not defined 에러가 발생했습니다.
이는 next/dynamic과 ssr: false 옵션을 사용해, 해당 컴포넌트를 클라이언트 사이드에서만 로드하도록 하여 해결했습니다.
3. 페이지 metaData 추가
스크린샷 (선택)
💬리뷰 요구사항(선택)
Summary by CodeRabbit
릴리스 노트
새 기능
버그 수정
성능 개선